#include "ProcInfoUtils.h"
#include <QFile>
#include <QtMath>
#include <QTextStream>

#define PROC_CPU        "/proc/stat"
#define PROC_MEMINFO    "/proc/meminfo"
#define PROC_NET_DEV    "/proc/net/dev"
#define PROC_UPTIME     "/proc/uptime"

ProcInfoUtils::ProcInfoUtils()
: old_cpuAll(0)
, old_cpuFree(0)
, old_uploadAll(0)
, old_downloadAll(0)
{
    
}

QString ProcInfoUtils::convertRateUnits(ProcInfoUtils::SpeedRateUnit &unit)
{
    switch (unit) {
    case SpeedRateBit:
        return QString("bit/s");
    case SpeedRateByte:
        return QString("b/s");
    case SpeedRateKb:
        return QString("kb/s");
    case SpeedRateMb:
        return QString("mb/s");
    case SpeedRateGb:
        return QString("gb/s");
    case SpeedRateTb:
        return QString("tb/s");
    default:
        return QString("");
    }
}

QString ProcInfoUtils::autoRateUnits(long speed, ProcInfoUtils::SpeedRateUnit &unit, double &sp)
{
    sp = 0;
    if (speed >= 0 && speed < qPow(2,10)) { // qPow(2,10) = 1024b
        unit = SpeedRateByte;
        sp = speed;
    } else if (speed >= qPow(2,10) && speed < qPow(2,20)) { // qPow(2,20) = 1024k
        unit = SpeedRateKb;
        sp =  (speed /  qPow(2,10) * 1.0);
    } else if (speed >= qPow(2,20) && speed < qPow(2,30)) { // qPow(2,30) = 1024m
        unit = SpeedRateMb;
        sp =  (speed /  qPow(2,20) * 1.0);
    } else if (speed >= qPow(2,30) && speed < qPow(2,40)) { // qPow(2,40) = 1024g
        unit = SpeedRateGb;
        sp =  (speed /  qPow(2,30) * 1.0);
    } else if (speed >= qPow(2,40) && speed < qPow(2,50)) { // qPow(2,50) = 1024t
        unit = SpeedRateTb;
        sp =  (speed /  qPow(2,40) * 1.0);
    } else {                                                // qPow(2,60) = 1024z => ?
        unit = SpeedRateUnknow;
        sp =  -1;
    }
    speed = sp;
    return convertRateUnits(unit);
}


double ProcInfoUtils::getLinuxCpuPercent()
{
    return readCpuPercent();
}

QList<double> ProcInfoUtils::getLinuxMemPercent()
{
    double mem_physical = readMemPhysicalPercent();
    double mem_virtual = readMemVirtualPercent();
    
    return QList<double>() << mem_physical << mem_virtual;
}

double ProcInfoUtils::getLinuxNetworkUp()
{
    long bytes = 0;
    double speed = 0;
    SpeedRateUnit unit = SpeedRateByte;

    // bytes = readNetworkSnapshotUp();
    // QString unitStr = autoRateUnits(bytes, unit, speed);
    // QString("↑:%1").arg(upspeed, 0, 'f', 2, QLatin1Char(' ')) + unitStr;
    // return speed;

    return readNetworkSnapshotUp();
}

double ProcInfoUtils::getLinuxNetworkDownload()
{
    long bytes = 0;
    double speed = 0;
    SpeedRateUnit unit = SpeedRateByte;

    // bytes = readNetworkSnapshotDown();
    // QString unitStr = autoRateUnits(bytes, unit, speed);
    // QString("↑:%1").arg(upspeed, 0, 'f', 2, QLatin1Char(' ')) + unitStr;
    // return speed;
    return readNetworkSnapshotDown();
}

double ProcInfoUtils::getLinuxUptime()
{
    return readUptime();
}


// zinface@zinface-PC:~$ cat /proc/stat  | grep cpu
// cpu  3507826 1283 1613816 67579694 58751 0 17705 0 0 0
// cpu0 451290 226 203888 8435772 7428 0 920 0 0 0
// cpu1 441084 183 203333 8441822 6801 0 1639 0 0 0
// cpu2 441769 157 203410 8445984 8455 0 1139 0 0 0
// cpu3 443894 142 202652 8446152 7537 0 1093 0 0 0
// cpu4 409728 118 200072 8458483 7569 0 5267 0 0 0
// cpu5 437691 149 197765 8458739 6853 0 900 0 0 0
// cpu6 439500 134 200032 8447919 7041 0 5902 0 0 0
// cpu7 442869 172 202661 8444820 7065 0 843 0 0 0
double ProcInfoUtils::readCpu(CpuMode mode)
{
    long time = 0;
    
    QFile file(PROC_CPU); // /proc/stat
    file.open(QIODevice::ReadOnly | QIODevice::Text);
    
    QTextStream stream(&file);          
    QString line = stream.readLine();   // 'cpu  3507826 1283 1613816 67579694 58751 0 17705 0 0 0'
    QStringList list = line.split(QRegExp("\\s{1,}"));

    // 收集状态
    if (mode == ALL) {      // 1. 全部时间
        foreach (QString str, list) 
            time += str.toLong();
    } else {                // 2. 空闲时间
        time = list.at(4).toLong();
    }

    file.close();

    return time;
}


double ProcInfoUtils::readCpuPercent()
{

    long time_all = readCpu(ALL);
    long time_free = readCpu(FREE);

    double percent;

    long use_time = (time_all - time_free);
    long old_use_time = (old_cpuAll - old_cpuFree);
    long percent_all = (time_all - old_cpuAll);
    long percent_free = (time_free - old_cpuFree);

    // 2. 计算 cpu 使用率： (全部时间 - 空闲时间) / 全部时间
    percent = (use_time - old_use_time) * 100.0 / percent_all;
    
    // 3. 存储状态 - 保存为旧状态
    old_cpuAll = time_all;
    old_cpuFree = time_free;

    return percent;
}

double ProcInfoUtils::readCpuPercentSnapshot()
{
    
}

// zinface@zinface-PC:~$ cat /proc/meminfo 
// MemTotal:       38956036 kB      // PHYSICAL ALL
// MemFree:         1306512 kB      // PHYSICAL FREE ALL
// MemAvailable:   17942736 kB
// Buffers:         2046216 kB
// Cached:         14309876 kB
// SwapCached:            0 kB
// Active:          7263116 kB
// Inactive:       26388668 kB
// Active(anon):     713312 kB
// Inactive(anon): 18541632 kB
// Active(file):    6549804 kB
// Inactive(file):  7847036 kB
// Unevictable:         416 kB
// Mlocked:             416 kB
// SwapTotal:             0 kB      // VIRTUAL ALL
// SwapFree:              0 kB      // VIRTUAL FREE ALL
// Dirty:             54192 kB
// Writeback:             0 kB
// AnonPages:      17296376 kB
// Mapped:          1657164 kB
// Shmem:           2044688 kB
// KReclaimable:    2760084 kB
// Slab:            3157360 kB
// SReclaimable:    2760084 kB

QList<double> ProcInfoUtils::readMem(MemMode mode) {

    double memoryAll = 0;
    double memory = 0;

    QFile file(PROC_MEMINFO);
    file.open(QIODevice::ReadOnly | QIODevice::Text);
    QTextStream stream(&file);

    long buff[16] = {0};
    for (int i = 0; i <= 15; ++i) {
        QString line = stream.readLine();
        QStringList list = line.split(QRegExp("\\s{1,}"));
        buff[i] = list.at(1).toLong();
    }
    if (mode == PHYSICAL) {         // PHYSICAL
        memoryAll = buff[0];
        memory = buff[0] - buff[2];
    } else {                        // VIRTUAL
        memoryAll = buff[14];
        memory = buff[14] - buff[15];
    }

    file.close();

    return QList<double>() << memory << memoryAll;
}

double ProcInfoUtils::readMemPercent(MemMode mode)
{
    double memory = 0;
    double memoryAll = 0;

    QList<double> memList = readMem(mode);
    memory = memList.at(0);
    memoryAll = memList.at(1);

    return (memory * 100.0 / memoryAll);
}

double ProcInfoUtils::readMemPhysicalPercent() {
    return readMemPercent(PHYSICAL);
}
double ProcInfoUtils::readMemVirtualPercent() {
    return readMemPercent(VIRTUAL);
}

// zinface@zinface-PC:~$ cat /proc/net/dev
// Inter-|   Receive                                                |  Transmit
//  face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
//     lo: 4112965538 1817828    0    0    0     0          0         0 4112965538 1817828    0    0    0     0       0          0
// wlp1s0: 5138671304 3912427    0 75719    0     0          0         0 137609139 1533178    0    0    0     0       0          0

// ignore 'Inter-'
// ignore ' face '
// upload   col 9
// download col 1
long ProcInfoUtils::readNetwork(NetworkMode mode)
{
    long bytesAll = 0;
    long bytes = 0;

    QFile file(PROC_NET_DEV);
    file.open(QIODevice::ReadOnly | QIODevice::Text);
    QTextStream stream(&file);
    QString line = stream.readLine();
    line  = stream.readLine();      // ignore 'Inter-'
    line  = stream.readLine();      // ignore ' face '

    while (!line.isNull()) {    // '  lo: 4112965538 ...'
        line = line.trimmed();  // 'lo: 4112965538 ...'
        QStringList list = line.split(QRegExp("\\s{1,}"));   // 匹配任意 大于等于1个的 空白字符

        if (!list.isEmpty()) {
            if (mode == UP) {
                bytes = list.at(9).toLong(); // upload
            } else {
                bytes = list.at(1).toLong(); // download
            }
        }

        bytesAll += bytes;
        line  = stream.readLine();
    }

    return bytesAll;
}

double ProcInfoUtils::readNetworkUp()
{
    return readNetwork(UP);
}

double ProcInfoUtils::readNetworkDown()
{
    return readNetwork(DOWN);
}

double ProcInfoUtils::readNetworkSnapshotUp()
{
    long snapshotBytes = 0;
    long bytes = readNetworkUp();
    
    snapshotBytes = bytes - old_uploadAll;
    old_uploadAll = bytes;

    return snapshotBytes;
}

double ProcInfoUtils::readNetworkSnapshotDown()
{
    long snapshotBytes = 0;
    long bytes = readNetworkDown();
    
    snapshotBytes = bytes - old_downloadAll;
    old_downloadAll = bytes;
    
    return snapshotBytes;
}

//    zinface@zinface-PC:~$ cat /proc/uptime
//    96557.08 718567.54
double ProcInfoUtils::readUptime()
{
    int uptime;
    QFile file("/proc/uptime"); // /proc/uptime
    file.open(QIODevice::ReadOnly | QIODevice::Text);
    QTextStream stream(&file);
    uptime = qFloor(stream.readLine().split(QRegExp("\\s{1,}")).at(0).toDouble());
    file.close();

    return uptime;
}